﻿using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Runtime;
using System;
using System.Collections.Generic;

namespace QdArch
{

    public class QdDoor : LcComponent3d
    {
        public static Shape2dProvider ShapeProvider;
        public static Solid3dProvider SolidProvider;
        public static ParameterSetDefinition ParamsDefinition;
        static QdDoor()
        {
            ShapeProvider = Shape2dManager.LoadProvider(ArchCategory.Door.ToString());

            ParamsDefinition = new ParameterSetDefinition() {
                 new ParameterDefinition
                 {
                     Name = "Thickness",
                     DataType= LcDataType.Double,
                 },
                 new ParameterDefinition
                 {
                     Name = "Width",
                      DataType= LcDataType.Double,
                 },
                 new ParameterDefinition
                 {
                     Name = "Height",
                     DataType= LcDataType.Double,
                 },
                 new ParameterDefinition
                 {
                     Name = "Bottom",
                     DataType= LcDataType.Double,
                 },
                 new ParameterDefinition
                 {
                     Name = "IsLeft",
                     DataType= LcDataType.Bool,
                 },
                 new ParameterDefinition
                 {
                     Name = "IsNear",
                     DataType= LcDataType.Bool,
                 },
            };

        }


        public QdDoor()
        {
            base.Category = ArchCategory.Door.ToString();
            this.Uuid = Guid.NewGuid().ToString();
        }
        public QdDoor(string subCategory, string name) : this()
        {
            base.SubCategory = subCategory;
            base.Name = name;
            this.Shape2D = ShapeProvider.Shapes.GetShape(name);
        }

    }

    public class QdDoorRef : LcComponentRef, IElement3D
    {

        public LcParameterSet Parameters { get; set; }
        public QdDoor Door { get; set; }
        /// <summary>
        /// 在墙上用中心插入
        /// </summary>
        public QdWall Wall { get; set; }
        //public LcLine Wall { get; set; }
        public Vector2d StartPoint { get; set; }
        public Vector2d EndPoint { get; set; }
        public Vector2d ControlPoint { get; set; }

        public double Thickness
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Thickness"]);
            }
            set
            {
                this.Parameters["Thickness"] = value;
            }
        }
        public double Width
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Width"]);
            }
            set
            {
                this.Parameters["Width"] = value;
            }
        }
        public double Height
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Height"]);
            }
            set
            {
                this.Parameters["Height"] = value;
            }
        }
        public double Bottom
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Bottom"]);
            }
            set
            {
                this.Parameters["Bottom"] = value;
            }
        }

        public bool IsLeft
        {
            get
            {
                return Convert.ToBoolean(this.Parameters["IsLeft"]);
            }
            set
            {
                this.Parameters["IsLeft"] = value;
            }
        }
        public bool IsNear
        {
            get
            {
                return Convert.ToBoolean(this.Parameters["IsNear"]);
            }
            set
            {
                this.Parameters["IsNear"] = value;
            }
        }
        public override Matrix3d Matrix
        {
            get
            {
                double angle = 0;
                if (this.Wall != null)
                {
                    angle = this.Wall.BaseLine.Dir.Angle();
                }
                var matrix = Matrix3d.Translate(this.InsertPoint) * Matrix3d.RotateInRadian(angle);
                return matrix;
            }
        }
        public QdDoorRef()
        {
            this.Type = ArchElementType.Door;
        }
        public override Box2d GetBoundingBox()
        {
            if (this.Parameters == null || this.Parameters.Values.Length == 0)
            {
                return Box2d.Empty;
            }

            var center = this.InsertPoint - new Vector2d(Width, Width);
            return new Box2d(center, Width * 2, Width * 2);

        }

        private List<ICurve2d> _shapeCurves;
        public List<ICurve2d> ShapeCurves
        {
            get
            {
                if (_shapeCurves == null)
                {
                    _shapeCurves = this.Door?.Shape2D?.Generate(this.Parameters);
                }
                return _shapeCurves;
            }
        }

        public void ResetShapeCurves()
        {
            this._shapeCurves = this.Door?.Shape2D?.Generate(this.Parameters);
        }

        public void ResetControlPoint()
        {
            var isRight = !this.IsLeft; //是否在插入点右侧
            var isUp = !this.IsNear; //是否在墙线上方，即墙线左侧

            //第三个点的旋转角度， 经观察发现三个点在半径相同的同一个圆上，圆心为doorRef.InsertPoint
            double angle = this.Wall.BaseLine.Dir.Angle();
            double angle45 = Math.PI / 4;
            if (isRight && isUp)
            {
                angle += angle45;
            }
            else if (!isRight && isUp)
            {
                angle += Math.PI / 2 + angle45;
            }
            else if (!isRight && !isUp)
            {
                angle += Math.PI + angle45;
            }
            else if (isRight && !isUp)
            {
                angle += Math.PI * 1.5 + angle45;
            }

            var lineDir = Vector2d.RotateInRadian(new Vector2d(1, 0), angle);
            var lineOffset = lineDir * (this.Width / 2);

            this.ControlPoint = this.InsertPoint + lineOffset;
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            Box2d thisBox = this.BoundingBox;
            if (!thisBox.IntersectWith(testPoly.BoundingBox))
            {
                return false;
            }

            var shape2d = this.Door.Shape2D;
            var wallLine = this.Wall.BaseLine;
            var angle = wallLine.Dir.Angle();

            var curves = this.ShapeCurves;
            foreach (var curve in curves)
            {
                if (curve.Type == Curve2dType.Line2d)
                {
                    var line = curve as Line2d;
                    var newLine = line.ApplyMatrix(this.Matrix);
                    if (GeoUtils.IsPolygonIntersectLine(testPoly.Points, newLine.Start, newLine.End)
                     || GeoUtils.IsPolygonContainsLine(testPoly.Points, newLine.Start, newLine.End))
                    {
                        return true;
                    }
                }
                else if (curve.Type == Curve2dType.Arc2d)
                {
                    var arc = curve as Arc2d;
                    var newArc = new Arc2d();
                    newArc.Radius = arc.Radius;
                    newArc.Center = this.Matrix.MultiplyPoint(arc.Center);
                    newArc.StartAngle = arc.StartAngle + Utils.RadianToDegree(angle);
                    newArc.EndAngle = arc.EndAngle + Utils.RadianToDegree(angle);

                    if (GeoUtils.IsPolygonIntersectArc(testPoly.Points, newArc))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            Box2d thisBox = this.BoundingBox;
            if (!testPoly.BoundingBox.Contains(thisBox))
            {
                return false;
            }
            return true;
        }

        public override LcElement Clone()
        {
            var clone = Document.CreateObject<QdDoorRef>();
            clone.Copy(this);
            clone.Initilize(this.Document);
            return clone;
        }

        public override void Copy(LcElement src)
        {
            base.Copy(src);
            var doorRef = (QdDoorRef)src;
            var pset = QdDoor.ParamsDefinition.New();
            pset["Thickness"] = doorRef.Parameters["Thickness"];
            pset["Width"] = doorRef.Parameters["Width"];
            pset["Height"] = doorRef.Parameters["Height"];
            pset["Bottom"] = doorRef.Parameters["Bottom"];
            pset["IsLeft"] = doorRef.Parameters["IsLeft"];
            pset["IsNear"] = doorRef.Parameters["IsNear"];
            this.Parameters = pset;
            this.Door = doorRef.Door;
            this.Wall = doorRef.Wall;
            this.StartPoint = doorRef.StartPoint.Clone();
            this.EndPoint = doorRef.EndPoint.Clone();
            this.ControlPoint = doorRef.ControlPoint.Clone();
            this.Thickness = doorRef.Thickness;
            this.Width = doorRef.Width;
            this.Height = doorRef.Height;
            this.Bottom = doorRef.Bottom;
            this.IsNear = doorRef.IsNear;
            this.IsLeft = doorRef.IsLeft;
        }
    }
}